<?php
// +----------------------------------------------------------------------
// | KITEGO-Admin「开箱即用」「人人全栈」
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2024 https://www.kitego.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed KITEGO并不是自由软件，未经许可不能去掉KITEGO相关版权
// +----------------------------------------------------------------------
// | Author: KITEGO Team <bd@kitego.cn>
// +----------------------------------------------------------------------

namespace kitego\utils;

use Godruoyi\Snowflake\Snowflake;
use kitego\services\common\CacheService;

class SnowflakeUtil
{
    /**
     * 生成雪花算法ID
     */
    public static function makeSnowflakeId(): string
    {
        static $snowflake = null;

        if ($snowflake === null) {
            $dataCenterId = config('snowflake.datacenter_id', 1);
            $workerId = config('snowflake.worker_id', 1);
            $startTime = config('snowflake.start_time', '2025-01-01 00:00:00');
            $startTimestamp = strtotime($startTime) * 1000; // 毫秒时间戳

            // 如果未配置工作节点ID，尝试从缓存获取
            if (empty($workerId)) {
                $workerId = static::getDistributedWorkerId();
            }

            // 确保节点ID在有效范围内
            $dataCenterId = max(0, min(31, (int)$dataCenterId)); // 数据中心ID范围: 0-31
            $workerId = max(0, min(31, (int)$workerId));         // 工作节点ID范围: 0-31

            $snowflake = new Snowflake($workerId, $dataCenterId);
            $snowflake->setStartTimeStamp($startTimestamp);
        }

        // 异常重试机制
        $maxRetries = 3;
        for ($i = 0; $i < $maxRetries; $i++) {
            try {
                return $snowflake->id();
            } catch (\Exception $e) {
                kg_file_put_contents('snow_flake_util', [sprintf(
                    '雪花算法生成ID失败 (尝试 %d/%d): %s',
                    $i + 1,
                    $maxRetries,
                    $e->getMessage()
                )]);

                // 指数退避策略
                usleep(10000 * pow(2, $i)); // 第一次等待10ms，第二次20ms，第三次40ms
            }
        }

        exception('生成雪花ID失败，超出最大重试次数');
    }

    /**
     * 获取分布式环境下的工作节点ID
     *
     * 使用缓存自增生成唯一ID
     */
    private static function getDistributedWorkerId(): int
    {
        $cacheKey = 'snowflake:worker_id';
        $cacheTtl = 3600; // 缓存有效期1小时

        // 尝试从缓存读取
        $workerId = CacheService::get($cacheKey);
        if ($workerId !== null) {
            return (int)$workerId;
        }

        try {
            // 使用缓存原子操作生成唯一ID
            $workerId = CacheService::store('redis')->incr($cacheKey);
            if ($workerId === 1) {
                // 首次创建设置过期时间
                CacheService::store('redis')->expire($cacheKey, $cacheTtl);
            }

            return $workerId % 32;
        } catch (\Exception $e) {
            // 缓存不可用时使用随机ID（不推荐，但可保证服务可用性）
            kg_file_put_contents('snow_flake_util', ['无法获取分布式工作节点ID: ' . $e->getMessage()]);

            return rand(0, 31);
        }
    }
}